home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / DTS.Draw / Window.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  49.9 KB  |  1,678 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* This file contains the code for the document procedure pointers for the main DTS.Draw
  20. ** document.  DTS.Draw currently only supports one type of file-based documents,
  21. ** type 'DDOC'. */
  22.  
  23. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  24.  
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29.  
  30.  
  31. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  32. #include "App.defs.h"        /* Get various application definitions.            */
  33. #include "App.protos.h"        /* Get the prototypes for application.            */
  34.  
  35. #ifndef __ERRORS__
  36. #include <Errors.h>
  37. #endif
  38.  
  39. #ifndef __FONTS__
  40. #include <Fonts.h>
  41. #endif
  42.  
  43. #ifndef __RESOURCES__
  44. #include <Resources.h>
  45. #endif
  46.  
  47. #ifndef __TOOLUTILS__
  48. #include <ToolUtils.h>
  49. #endif
  50.  
  51. #ifndef __TREEOBJ2__
  52. #include "TreeObj2.h"
  53. #endif
  54.  
  55. #ifndef __UTILITIES__
  56. #include "Utilities.h"
  57. #endif
  58.  
  59.  
  60.  
  61. /*****************************************************************************/
  62.  
  63.  
  64.  
  65. typedef struct {
  66.     TreeObjHndl    root;
  67.     short        cnum;
  68. } LayerDrawInfo;
  69.  
  70. Boolean            gNoDefaultDocument = false;
  71.                     /* Set to true if app should boot with no default document. */
  72.                     /* This tells DTS.Lib..framework what you want. */
  73.  
  74. OSType            gAppWindowType = kDocFileType;    /* Main document type. */
  75. long            gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  76.  
  77. short            gMinVersion = kMinVersion;    /* Minimum document version app can support. */
  78. short            gMaxVersion = kMaxVersion;    /* Maximum document version app can support. */
  79.                                             /* More informing DTS.Lib..framework. */
  80.  
  81. extern short    gPrintPage;                    /* Non-zero means we are printing. */
  82.                                             /* DTS.Lib..framework global. */
  83.  
  84. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  85. extern CursPtr        gCursorPtr;                /* to know about these things. */
  86.                                             /* Above are DTS.Lib..framework globals. */
  87.  
  88. static void        AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click);
  89. static void        SlideSelection(FileRecHndl frHndl, ClickInfo *click);
  90. static OSErr    WindowLayerProc(LayerObj theLayer, short message);
  91. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  92. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  93. static void        DrawPageGrid(void);
  94.  
  95. #define kDataAreaPadding 0
  96.  
  97.  
  98. /*****************************************************************************/
  99. /*****************************************************************************/
  100.  
  101.  
  102.  
  103. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  104.  
  105. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  106. ** You are passed an empty region.  You are supposed to add any custom frame
  107. ** parts that this document uses.  Typically there are no frame portions, as
  108. ** they are accounted for in other ways.  The scrollbars and grow icon will
  109. ** automatically be contributed to the calculation of the frame region.
  110. ** If you use sidebars, these are also added in automatically.  This is only
  111. ** used if the frame region is more complicated than can automatically be
  112. ** handled.  So, almost always, you will simply leave the region empty. */
  113.  
  114. #pragma segment TheDoc
  115. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  116. {
  117. #ifndef __MWERKS__
  118. #pragma unused (frHndl, window, rgn)
  119. #endif
  120. }
  121.  
  122.  
  123.  
  124. /*****************************************************************************/
  125.  
  126.  
  127.  
  128. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  129.  
  130. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  131. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  132. ** further process the click. */
  133.  
  134. #pragma segment TheDoc
  135. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  136. {
  137. #ifndef __MWERKS__
  138. #pragma unused (firstClick)
  139. #endif
  140.  
  141.     FileRecHndl        frHndl;
  142.     ControlHandle    ctl;
  143.     short            dblClick, shiftMod;
  144.     Point            contOrg;
  145.     Boolean            selected;
  146.     TreeObjHndl        hitObj, root;
  147.     ClickInfo        click;
  148.  
  149.     SetPort(window);
  150.  
  151.     if (IsCtlEvent(window, event, &ctl, &dblClick)) return;
  152.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  153.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  154.         ** controls in the content besides the document scrollbars. */
  155.  
  156.     frHndl = (FileRecHndl)GetWRefCon(window);
  157.     if ((*frHndl)->fileState.readOnly) return;
  158.         /* Don't allow changes if read-only. */
  159.  
  160.     /* If none of the above resolved why we are here, then the click was in the
  161.     ** actual drawing area of the content, and we have some serious work to do. */
  162.  
  163.     GetContentOrigin(window, &contOrg);
  164.     SetOrigin(contOrg.h, contOrg.v);
  165.         /* This sets the origin of the document area of the content to the position
  166.         ** indicated by the document scrollbars. */
  167.  
  168.     click.localEvent = *event;
  169.     GlobalToLocal(&click.localEvent.where);
  170.         /* Who wants to work in global coordinates, anyway... */
  171.  
  172.     if (!GetTool()) {            /* If no specific tool (arrow tool)... */
  173.  
  174.         click.message = HITTESTGRABBER;
  175.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  176.             /* See if the user clicked on a grabber for a selected object. */
  177.  
  178.         if (!hitObj) {        /* If user didn't click on a grabber, see if they hit an object. */
  179.             click.message = HITTESTOBJ;
  180.             hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  181.         }
  182.  
  183.         shiftMod = click.localEvent.modifiers & shiftKey;
  184.             /* Find out if the user was holding down the shift key. */
  185.  
  186.         if (click.message == HITTESTGRABBER)
  187.             shiftMod = 0;
  188.                 /* Pretent that the shift key isn't held down if they are on a grabber.  If
  189.                 ** they are on a grabber, you want to do a constrain with the shift key. */
  190.  
  191.         if (hitObj) {        /* User hit either a grabber or the object itself. */
  192.  
  193.             selected = mDerefCommon(hitObj)->selected;
  194.                 /* See if the object hit, independent of type, was selected. */
  195.  
  196.             if ((!selected) && (!shiftMod))        /* The object isn't selected, and */
  197.                 DoTreeSelect(root, SELECTOFF);    /* the shift key isn't held down, */
  198.                                                 /* so deselect all other objects. */
  199.  
  200.             click.message = CLICKSELECT;
  201.             if ((!selected) || (shiftMod))
  202.                 DoTreeObjMethodClipped(hitObj, CLICKMESSAGE, (long)&click);
  203.                     /* If the object isn't selected, or if the shift key is down, change
  204.                     ** the select state of the object that was clicked on. */
  205.  
  206.             click.localEvent.modifiers ^= shiftMod;
  207.                 /* Turn off the shift modifier, which makes the rest of the
  208.                 ** handling of the object easier. */
  209.  
  210.             if (mDerefCommon(hitObj)->selected) {    /* If object selected, do something with it. */
  211.                 if (click.grabber == -1)
  212.                     SlideSelection(frHndl, &click);
  213.                 else
  214.                     AddOrSizeObj(frHndl, hitObj, &click);
  215.             }            /* The click could have been used to shift-click deselect an object,
  216.                         ** so only do something with the object if it is selected.  We  either
  217.                         ** resize the object (if a grabber was clicked on) or slide the selected
  218.                         ** objects (if an object was clicked on). */
  219.  
  220.         }
  221.         else {                                    /* No object was clicked on... */
  222.  
  223.             if (!shiftMod)                            /* If not a shift-click, */
  224.                 DoTreeSelect(root, SELECTOFF);        /* deselect everything.  */
  225.  
  226.             AddOrSizeObj(frHndl, hitObj, &click);    /* Do extend select. */
  227.         }
  228.     }
  229.     else {
  230.         click.message = HITTESTGRABBER;
  231.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  232.             /* See if the user clicked on a grabber for a selected object. */
  233.         AddOrSizeObj(frHndl, hitObj, &click);
  234.             /* No object clicked on, so add an object. */
  235.     }
  236. }
  237.  
  238.  
  239.  
  240. /*****************************************************************************/
  241.  
  242.  
  243.  
  244. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  245.  
  246. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  247. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  248. **
  249. ** 1) If you handle the key, return(true).  This completes the key handling.
  250. ** 2) If you don't handle the key, you return false.  However, there are two
  251. **    situations for not handling the key:
  252. **      a) You want someone else to.
  253. **      b) You want nobody else to look at the key.
  254. **    This is what the boolean passThrough is for.  If you wish the next window
  255. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  256. **    is already initialized to false, which is the common case, so you only have
  257. **    to worry about setting it true.
  258. **
  259. ** If you have a window that never processes keys and always passes them through,
  260. ** just set the contentKeyProc to nil.  This will indicate to the application
  261. ** framework that all keys should be passed through this window.  DTS.Draw has
  262. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  263. ** to document windows. */
  264.  
  265. #pragma segment TheDoc
  266. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  267. {
  268. #ifndef __MWERKS__
  269. #pragma unused (passThrough)
  270. #endif
  271.  
  272.     char        key;
  273.     FileRecHndl    frHndl;
  274.  
  275.     key = event->message & charCodeMask;
  276.     if (key != 8) return(false);
  277.         /* The only key we handle is the delete key.  leave for all the rest. */
  278.  
  279.     SetPort(window);
  280.  
  281.     frHndl = (FileRecHndl)GetWRefCon(window);
  282.     if ((*frHndl)->fileState.readOnly) return(false);
  283.         /* Don't allow changes if read-only. */
  284.  
  285.     DoDelete(frHndl);
  286.  
  287.     return(true);
  288. }
  289.  
  290.  
  291.  
  292. /*****************************************************************************/
  293.  
  294.  
  295.  
  296. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  297.  
  298. /* Draw application specific content (Called by DoDrawFrame).
  299. **
  300. ** If your application has any custom frame areas, or if it uses sidebars,
  301. ** this is the function that you would put the frame drawing code.  The
  302. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  303. ** Just do the sidebar and custom areas here. */
  304.  
  305. #pragma segment TheDoc
  306. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  307. {
  308.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  309.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  310.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  311.  
  312.     BeginFrame(window);
  313.     DoDrawControls(window, activate);
  314.     EndFrame(window);
  315. }
  316.  
  317.  
  318.  
  319. /*****************************************************************************/
  320.  
  321.  
  322.  
  323. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  324.  
  325. /* Frees up any application-specific memory in the document.  This is called by
  326. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  327. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  328. **
  329. ** The document may have a bunch of handles off the main handle of the document.
  330. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  331. ** the ram for the main handle of the document, so release everything else
  332. ** here, or you will have a memory leak.
  333. **
  334. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  335. ** hierarchical document (see TreeObj package). */
  336.  
  337. #pragma segment TheDoc
  338. OSErr    FreeDocument(FileRecHndl frHndl)
  339. {
  340.     return(DefaultFreeDocument(frHndl));
  341. }
  342.  
  343.  
  344.  
  345. /*****************************************************************************/
  346.  
  347.  
  348.  
  349. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  350.  
  351. /* Any additional window disposal tasks can be handled here. */
  352.  
  353. #pragma segment TheDoc
  354. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  355. {
  356. #ifndef __MWERKS__
  357. #pragma unused (window)
  358. #endif
  359.  
  360.     WindowPtr    ww;
  361.     FileRecHndl    ff;
  362.  
  363.     if ((*frHndl)->fileState.sfType == kDocFileType) {
  364.         ManageDragHandlers(window, false);
  365.         for (ww = nil; (ww = GetNextWindow(ww, 0)) != nil;) {
  366.             ff = (FileRecHndl)GetWRefCon(ww);
  367.             if ((*ff)->fileState.sfType == kViewHierFileType) {
  368.                 if ((*frHndl)->d.doc.root == (*ff)->d.doc.root) {
  369.                     DisposeOneWindow(ww, kClose);
  370.                     ww = nil;
  371.                 }
  372.             }
  373.         }
  374.     }
  375.  
  376.     return(noErr);
  377. }
  378.  
  379.  
  380.  
  381. /*****************************************************************************/
  382.  
  383.  
  384.  
  385. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  386.  
  387. /* Image the document into the current port.
  388. **
  389. ** The only thing tricky about this function is that it needs to key off of
  390. ** the global variable gPrintPage.  gPrintPage is the current page that is
  391. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  392. **
  393. ** For when printing:
  394. **
  395. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  396. ** the page there are no more pages, you should set gPrintPage to 0.  This
  397. ** indicates to the print loop that the end of the document has been reached.
  398. ** Even if the user indicated in the job dialog to print more pages, setting
  399. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  400. ** because the print loop can't know when printing is done.  The imaging procedure
  401. ** is the logical one to state when everything has been imaged. */
  402.  
  403. #pragma segment TheDoc
  404. OSErr    ImageDocument(FileRecHndl frHndl)
  405. {
  406.     LayerDrawInfo    drawInfo;
  407.     LayerObj        wlyr, blyr;
  408.  
  409.     drawInfo.root = (*frHndl)->d.doc.root;
  410.         /* If there isn't a background layer, then all objects are drawn into
  411.         ** the work layer, and therefore we don't need to fill in the cnum field
  412.         ** of the drawInfo.  The WorkLayerProc will notice that there isn't a
  413.         ** layer behind it and will automatically do the right thing. */
  414.  
  415.     if (gPrintPage) {
  416.         DoTreeDraw(drawInfo.root, DRAWOBJ);        /* Draw the page. */
  417.         gPrintPage = 0;                            /* We only support one page in this sample. */
  418.         return(noErr);
  419.     }
  420.  
  421.     NewLayer(&wlyr, nil, WindowLayerProc, (*frHndl)->fileState.window, 0, (long)&drawInfo);
  422.         /* Create a layer object for the window. */
  423.  
  424.     NewLayer(&blyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  425.         /* Create a background layer for drawing of all of the objects. */
  426.  
  427.     InvalLayer(wlyr, GetEffectiveDstRect(wlyr), false);
  428.         /* We want to draw the entire contents. */
  429.  
  430.     UpdateLayer(wlyr);
  431.         /* Update what's invalid, which is everything.  All the objects are drawn into
  432.         ** the background layer, and then the background layer is transferred into the
  433.         ** window. */
  434.  
  435.     DisposeThisAndBelowLayers(wlyr);
  436.         /* Clean up what we created. */
  437.  
  438.     return(noErr);
  439. }
  440.  
  441.  
  442.  
  443. /*****************************************************************************/
  444.  
  445.  
  446.  
  447. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  448.  
  449. /* This function does the remaining window initialization.
  450. **
  451. ** There may be additional content initialization for the window.  At this point,
  452. ** you have a window, but it is currently invisible.  If you return noErr, then
  453. ** the window will be set to the state indicated for that window.  Why this function?
  454. ** You may wish to add controls to the content of the window.  You may have a
  455. ** TextEdit record in the content.  All of these sort of things can't be created
  456. ** until there is a window to contain them.  First a document is read in, and then
  457. ** if the document creation succeeds, a window is created for that document.
  458. ** At this point we have a document, and we are on our way to having a window.
  459. ** All that remains is any additional content initialization.  Do it, return
  460. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  461. ** and the incomplete window will be disposed of. */
  462.  
  463. #pragma segment TheDoc
  464. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  465. {
  466.     OSErr        err;
  467.     Rect        rct;
  468.     TreeObjHndl    root;
  469.  
  470.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  471.  
  472.     rct = GetDataArea(root = (*frHndl)->d.doc.root);
  473.     SetDataArea(root, rct.right, rct.bottom);
  474.     ManageDragHandlers(window, true);
  475.  
  476.     return(err);
  477. }
  478.  
  479.  
  480.  
  481. /*****************************************************************************/
  482.  
  483.  
  484.  
  485. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  486.  
  487. /* The code below assumes that you are using the hierarchical document package.
  488. ** If you are, the entire hierarchical document is read in with just these two
  489. ** calls.  If you don't use it, you are on your own.  See DTS.Chat for an
  490. ** example of an application that uses the DTS.framework without the hierarchical
  491. ** document package. */
  492.  
  493. #pragma segment TheDoc
  494. OSErr    ReadDocument(FileRecHndl frHndl)
  495. {
  496.     OSErr        err;
  497.     TreeObjHndl    root;
  498.  
  499.     err = DefaultReadDocument(frHndl);
  500.     root = (*frHndl)->d.doc.root;
  501.     mDerefRoot(root)->numSelected = 0;        /* User may have saved when objects were selected. */
  502.     if (!err)
  503.         DefaultReadDocumentFixup(frHndl);
  504.  
  505.     return(err);
  506. }
  507.  
  508.  
  509.  
  510. /*****************************************************************************/
  511.  
  512.  
  513.  
  514. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  515.  
  516. /* Resize application specific content (Called by ResizeWindow).
  517. **
  518. ** This gets called when a user does a zoom or window resizing operation.
  519. ** It is possible that things in the content need to be resized in conjunction
  520. ** with the resizing of the window. */
  521.  
  522. #pragma segment TheDoc
  523. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  524. {
  525. #ifndef __MWERKS__
  526. #pragma unused (window, oldh, oldv)
  527. #endif
  528.  
  529.     /* See DTS.Chat for a sample usage of this function. */
  530. }
  531.  
  532.  
  533.  
  534. /*****************************************************************************/
  535.  
  536.  
  537.  
  538. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  539.  
  540. /* Scroll application specific frame (Called by DoScrollFrame).
  541. **
  542. ** Some applications may need to scroll the "frame" of the document along
  543. ** with the document contents.  This is common for applications with rulers,
  544. ** or other similar sidebar items. */
  545.  
  546. #pragma segment TheDoc
  547. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  548. {
  549. #ifndef __MWERKS__
  550. #pragma unused (frHndl, window, dh, dv)
  551. #endif
  552. }
  553.  
  554.  
  555.  
  556. /*****************************************************************************/
  557.  
  558.  
  559.  
  560. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  561.  
  562. /* This gets called just prior to and just after an undo/redo operation is done. */
  563.  
  564. #pragma segment TheDoc
  565. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  566. {
  567.     WindowPtr    window;
  568.     TreeObjHndl    root;
  569.     Rect        rct;
  570.  
  571.     window = (*frHndl)->fileState.window;
  572.     root   = (*frHndl)->d.doc.root;
  573.  
  574.     if (!afterUndo)                            /* Before an undo operation we deselect everything. */
  575.         DoTreeSelect(root, SELECTOFF);        /* Only what was undone should end up selected. */
  576.  
  577.     if (afterUndo) {
  578.         rct = GetDataArea(root);
  579.         SetDataArea(root, rct.right, rct.bottom);
  580.         SetContentOrigin(window, contOrg.h, contOrg.v);
  581.             /* Undo may have a different document origin than where the user was.
  582.             ** Scroll the document back to where the edit we are undoing took place. */
  583.  
  584.         BeginContent(window);            /* Redraw the document to display */
  585.         DoImageDocument(frHndl);        /* the changes due to undo. */
  586.         EndContent(window);
  587.     }
  588.  
  589.     DoSetCursor(nil);
  590. }
  591.  
  592.  
  593.  
  594. /*****************************************************************************/
  595.  
  596.  
  597.  
  598. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  599.  
  600. /* This function is where you adjust the cursor to reflect the location in the
  601. ** document or window.  You have the additional input of gCursorRgn to deal
  602. ** with.  The way that the cursor handling works is as follows:
  603. ** 1) The application calls DoWindowCursor().
  604. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  605. **    It looks at the document's windowCursorProc and checks to see if the document
  606. **    has one.  If the document doesn't have one, then it assumes that that window
  607. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  608. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  609. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  610. **    is set to an arrow (for the non-document area of the screen).
  611. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  612. **    job is as follows:
  613. **    a) If the cursor is over a position that is determined by the window, then
  614. **       the proc removes other areas from gCursorRgn.  Note that it should not
  615. **       simply set the area to what it "thinks" is the correct area.  This window
  616. **       may not be the front-most.  Other windows will have already been subtracted
  617. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  618. **       and should be passed to WaitNextEvent calls in the application (already the case
  619. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  620. **       You should also return true, as the cursor has been determined.
  621. **    b) If the cursor is not over a position for this window, then you should
  622. **       return.  You will either pass back true or false.  If you don't wish
  623. **       windows behind this window to have a shot at cursor determination, then
  624. **       return true.  This states that the cursor is "determined".  It is, in the
  625. **       sense that no further determination will occur.  If you return false, then
  626. **       other windows get a shot at determining the cursor.
  627. **
  628. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  629. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  630. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  631. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  632. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  633. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  634. ** to the return result of DoSetResCursor(), and you will be set. */
  635.  
  636. #pragma segment TheDoc
  637. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  638. {
  639. #ifndef __MWERKS__
  640. #pragma unused (frHndl)
  641. #endif
  642.  
  643.     RgnHandle    frameRgn, contRgn;
  644.  
  645.     if (GetTool()) {
  646.         frameRgn = DoCalcFrameRgn(window);
  647.         contRgn  = NewRgn();
  648.         DiffRgn(((WindowPeek)window)->contRgn, frameRgn, contRgn);
  649.         DisposeRgn(frameRgn);
  650.         if (PtInRgn(globalPt, contRgn)) {
  651.             gCursorPtr = DoSetResCursor(addObjCursor);
  652.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  653.             DisposeRgn(contRgn);
  654.             return(true);
  655.         }
  656.         DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  657.         SectRgn(gCursorRgn, contRgn, gCursorRgn);
  658.         DisposeRgn(contRgn);
  659.     }
  660.  
  661.     SetCursor(gCursorPtr = &qd.arrow);
  662.     return(true);
  663. }
  664.  
  665.  
  666.  
  667. /*****************************************************************************/
  668.  
  669.  
  670.  
  671. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  672.  
  673. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  674. ** to give the application a chance to do any additional tasks related to
  675. ** a window closing.  DTS.Chat doesn't have anything else extra to do. */
  676.  
  677. #pragma segment TheDoc
  678. void    WindowGoneFixup(WindowPtr window)
  679. {
  680. #ifndef __MWERKS__
  681. #pragma unused (window)
  682. #endif
  683. }
  684.  
  685.  
  686.  
  687. /*****************************************************************************/
  688.  
  689.  
  690.  
  691. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  692.  
  693. /* The reverse function of ReadDocument. */
  694.  
  695. #pragma segment TheDoc
  696. OSErr    WriteDocument(FileRecHndl frHndl)
  697. {
  698.     return(DefaultWriteDocument(frHndl));
  699. }
  700.  
  701.  
  702.  
  703. /*****************************************************************************/
  704.  
  705.  
  706.  
  707. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  708.  
  709. #pragma segment TheDoc
  710. OSErr    DoOpenApplication(void)
  711. {
  712.     return(noErr);
  713. }
  714.  
  715.  
  716.  
  717. /*****************************************************************************/
  718.  
  719.  
  720.  
  721. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  722.  
  723. #pragma segment TheDoc
  724. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  725. {
  726.     Boolean        redrawMenuBar;
  727.     MenuHandle    menu;
  728.  
  729.     redrawMenuBar = false;
  730.  
  731.     switch (menuID) {
  732.         case mFile:
  733.             redrawMenuBar = DoAdjustFileMenu(window);
  734.             break;
  735.         case mEdit:
  736.             redrawMenuBar = DoAdjustEditMenu(window);
  737.             break;
  738.         case mArrange:
  739.             redrawMenuBar = DoAdjustArrangeMenu(window);
  740.             break;
  741.         case mOther:
  742.             redrawMenuBar = DoAdjustOtherMenu(window);
  743.             break;
  744.         default:
  745.             menu = GetMenuHandle(menuID);
  746.             if (menu)
  747.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  748.             break;
  749.     }
  750.  
  751.     return(redrawMenuBar);
  752. }
  753.  
  754.  
  755.  
  756. /*****************************************************************************/
  757.  
  758.  
  759.  
  760. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  761.  
  762. #pragma segment TheDoc
  763. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  764. {
  765. #ifndef __MWERKS__
  766. #pragma unused (window)
  767. #endif
  768.  
  769.     return(DoMenuCommand(menuID, menuItem));
  770. }
  771.  
  772.  
  773.  
  774. /*****************************************************************************/
  775. /*****************************************************************************/
  776. /*****************************************************************************/
  777.  
  778.  
  779.  
  780. #pragma segment DrawDoc
  781. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  782. {
  783.     OSErr        err;
  784.     TreeObjHndl    root;
  785.  
  786.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  787.         /* Create a document and root object to copy the file data into. */
  788.  
  789.     if (!err)
  790.         err = CopyChildren((*oldFrHndl)->d.doc.root, (**newFrHndl)->d.doc.root);
  791.             /* Copy the hierarchical data into the new file. */
  792.  
  793.     if (!err) {
  794.         root = (**newFrHndl)->d.doc.root;
  795.         DoFTreeMethod(root, UNDOMESSAGE, UNDOFROMDOC);
  796.         mDerefRoot(root)->numSelected = 0;
  797.     }        /* We have to be careful to use methods that don't try to draw
  798.             ** anything, as we don't have a window to draw into yet. */
  799.  
  800.     return(err);
  801. }
  802.  
  803.  
  804.  
  805. /*****************************************************************************/
  806.  
  807.  
  808.  
  809. /* This is called when a key event occurs and it is determined that it isn't
  810. ** a menu key. */
  811.  
  812. #pragma segment DrawDoc
  813. void    DoDelete(FileRecHndl frHndl)
  814. {
  815.     WindowPtr    window;
  816.     TreeObjHndl    root;
  817.     short        cnum;
  818.     Boolean        didDelete;
  819.  
  820.     root = (*frHndl)->d.doc.root;
  821.         /* All of the selected objects are children of the root, so all we have to do
  822.         ** is walk the children of the root looking for selected objects.  If the
  823.         ** document has grouping objects, which have sub-children, only the group
  824.         ** object is selected. */
  825.  
  826.     for (didDelete = false, cnum = (*root)->numChildren; cnum;) {
  827.         if (mDerefCommon(GetChildHndl(root, --cnum))->selected) {
  828.             if (!didDelete)
  829.                 NewDocumentUndo(frHndl);
  830.                     /* Make sure this delete event is posted in a separate undo from
  831.                     ** a possible previous delete event. */
  832.             DisposeChild(DELETE_EDIT, root, cnum);
  833.                 /* Post the delete of the child into the undo hierarchy. */
  834.             didDelete = true;
  835.                 /* Yes, we did change the document. */
  836.         }
  837.     }
  838.  
  839.     if (didDelete) {                            /* If something got deleted... */
  840.         window = (*frHndl)->fileState.window;
  841.         SetWindowDirty(window);                    /* Flag the document as dirty. */
  842.         BeginContent(window);                    /* Redraw the contents. */
  843.         DoImageDocument(frHndl);
  844.         EndContent(window);
  845.     }
  846. }
  847.  
  848.  
  849.  
  850. /*****************************************************************************/
  851.  
  852.  
  853.  
  854. #pragma segment DrawDoc
  855. void    DoArrange(FileRecHndl frHndl, short menuItem)
  856. {
  857.     TreeObjHndl    root;
  858.     short        cnum, cdir, cend;
  859.     WindowPtr    window;
  860.  
  861.     if (menuItem >= kGroup) {
  862.         switch (menuItem) {
  863.             case kGroup:
  864.                 DoGroup(frHndl);
  865.                 break;
  866.             case kUngroup:
  867.                 DoUngroup(frHndl);
  868.                 break;
  869.         }
  870.     }
  871.     else {
  872.         root = (*frHndl)->d.doc.root;
  873.  
  874.         cnum = 0;
  875.         cdir = 1;
  876.         cend = (*root)->numChildren;
  877.  
  878.         if (cend)
  879.             NewDocumentUndo(frHndl);
  880.  
  881.         if (menuItem >= kMoveBackward) {
  882.             cnum = cend - 1;
  883.             cdir = -1;
  884.             cend = -1;
  885.         }
  886.  
  887.         for (; cnum != cend; cnum += cdir) {
  888.             if (DoTreeObjMethod(GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  889.                 switch (menuItem) {
  890.                     case kMoveForward:
  891.                         if (cnum)
  892.                             MoveChild(MOVEFORWARD_EDIT, root, cnum, root, cnum - 1);
  893.                         break;
  894.                     case kMoveToFront:
  895.                         MoveChild(MOVETOFRONT_EDIT, root, cnum, root, 0);
  896.                         break;
  897.                     case kMoveBackward:
  898.                         MoveChild(MOVEBACKWARD_EDIT, root, cnum, root, cnum + 1);
  899.                         break;
  900.                     case kMoveToBack:
  901.                         MoveChild(MOVETOBACK_EDIT, root, cnum, root, -1);
  902.                         break;
  903.                 }
  904.             }
  905.         }
  906.     }
  907.     SetWindowDirty(window = (*frHndl)->fileState.window);
  908.  
  909.     BeginContent(window);
  910.     DoImageDocument(frHndl);
  911.     EndContent(window);
  912. }
  913.  
  914.  
  915.  
  916. /*****************************************************************************/
  917.  
  918.  
  919.  
  920. #pragma segment DrawDoc
  921. Rect    GetSelectedArea(TreeObjHndl root)
  922. {
  923.     TreeObjHndl    child;
  924.     short        cnum;
  925.     Rect        selectRct, rct;
  926.     Boolean        first;
  927.  
  928.     SetRect(&selectRct, 0, 0, 0, 0);
  929.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  930.         child = GetChildHndl(root, --cnum);
  931.         if (DoTreeObjMethod(child, GETSELECTMESSAGE, 0)) {
  932.             DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  933.             if (!EmptyRect(&rct)) {
  934.                 if (first) {
  935.                     selectRct = rct;
  936.                     first = false;
  937.                 }
  938.                 else UnionRect(&selectRct, &rct, &selectRct);
  939.             }
  940.         }
  941.     }
  942.     return(selectRct);
  943. }    
  944.  
  945.  
  946.  
  947. /*****************************************************************************/
  948.  
  949.  
  950.  
  951. #pragma segment DrawDoc
  952. Rect    GetDataArea(TreeObjHndl root)
  953. {
  954.     TreeObjHndl    child;
  955.     short        cnum;
  956.     Rect        areaRct, rct;
  957.     Boolean        first;
  958.  
  959.     SetRect(&areaRct, 0, 0, 0, 0);
  960.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  961.         child = GetChildHndl(root, --cnum);
  962.         DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  963.         if (!EmptyRect(&rct)) {
  964.             if (first) {
  965.                 areaRct = rct;
  966.                 first   = false;
  967.             }
  968.             else UnionRect(&areaRct, &rct, &areaRct);
  969.         }
  970.     }
  971.     return(areaRct);
  972. }    
  973.  
  974.  
  975.  
  976. /*****************************************************************************/
  977.  
  978.  
  979.  
  980. #pragma segment DrawDoc
  981. void    SetDataArea(TreeObjHndl root, short h, short v)
  982. {
  983.     if (h != kwNoChange) {
  984.         h /= (7 * 72);
  985.         ++h;
  986.         h *= (7 * 72);
  987.     }
  988.  
  989.     if (v != kwNoChange) {
  990.         v /= (10 * 72);
  991.         ++v;
  992.         v *= (10 * 72);
  993.     }
  994.  
  995.     SetDocSize(mDerefRoot(root)->frHndl, h, v);
  996. }    
  997.  
  998.  
  999.  
  1000. /*****************************************************************************/
  1001.  
  1002.  
  1003.  
  1004. #pragma segment DrawDoc
  1005. void    DoGroup(FileRecHndl frHndl)
  1006. {
  1007.     TreeObjHndl    root, group, child;
  1008.     short        gnum, cnum;
  1009.     Rect        groupRct;
  1010.  
  1011.     NewDocumentUndo(frHndl);
  1012.  
  1013.     root = (*frHndl)->d.doc.root;
  1014.     for (gnum = 0; gnum < (*root)->numChildren; ++gnum)
  1015.         if (DoTreeObjMethod(GetChildHndl(root, gnum), GETSELECTMESSAGE, 0)) break;
  1016.  
  1017.     if (!(group = NewChild(GROUP_EDIT, root, gnum, GROUPOBJ, 0))) return;
  1018.  
  1019.     groupRct = GetSelectedArea(root);
  1020.     for (cnum = (*root)->numChildren - 1; cnum > gnum; --cnum) {
  1021.         if (DoTreeObjMethod(child = GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  1022.             DoTreeObjMethodClipped(child, SETSELECTMESSAGE, SELECTOFF);
  1023.             MoveChild(GROUP_EDIT, root, cnum, group, 0);
  1024.         }
  1025.     }
  1026.  
  1027.     mDerefGroup(group)->group = groupRct;
  1028. }    
  1029.  
  1030.  
  1031.  
  1032. /*****************************************************************************/
  1033.  
  1034.  
  1035.  
  1036. #pragma segment DrawDoc
  1037. void    DoUngroup(FileRecHndl frHndl)
  1038. {
  1039.     TreeObjHndl    root, group;
  1040.     short        gnum, cnum;
  1041.  
  1042.     NewDocumentUndo(frHndl);
  1043.  
  1044.     root = (*frHndl)->d.doc.root;
  1045.     for (gnum = (*root)->numChildren; gnum;) {
  1046.         if (DoTreeObjMethod(group = GetChildHndl(root, --gnum), GETSELECTMESSAGE, 0)) {
  1047.             if ((*group)->type == GROUPOBJ) {
  1048.                 DoTreeSelect(group, SELECTOFF);
  1049.                 for (cnum = (*group)->numChildren; cnum;) {
  1050.                     MoveChild(UNGROUP_EDIT, group, --cnum, root, gnum + 1);
  1051.                     DoTreeSelect(GetChildHndl(root, gnum + 1), SELECTON);
  1052.                 }
  1053.                 DisposeChild(UNGROUP_EDIT, root, gnum);
  1054.             }
  1055.         }
  1056.     }
  1057. }    
  1058.  
  1059.  
  1060.  
  1061. /*****************************************************************************/
  1062. /*****************************************************************************/
  1063. /*****************************************************************************/
  1064.  
  1065.  
  1066.  
  1067. #pragma segment DrawDoc
  1068. void    AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click)
  1069. {
  1070.     TreeObjHndl        root, cobj, origHndl, oldHndl;
  1071.     WindowPtr        window;
  1072.     short            cnum, h, w, i;
  1073.     short            adding, extSelect;
  1074.     LayerObj        wlyr, wklyr, blyr;
  1075.     Rect            oldRct, newRct, rct, extRct1, extRct2, extRct3;
  1076.     OSErr            err;
  1077.     LayerDrawInfo    drawInfo;
  1078.     Boolean            tool, keepTool, newTool;
  1079.     Point            curMouse, org, oldOrg;
  1080.  
  1081.     root   = (*frHndl)->d.doc.root;
  1082.     window = (*frHndl)->fileState.window;
  1083.  
  1084.     tool      = GetTool();
  1085.     keepTool  = GetToolPersistence();
  1086.     adding    = false;
  1087.     extSelect = false;
  1088.     newTool   = false;
  1089.  
  1090.     if (!hndl) {        /* If hndl == nil, then we are adding a new object. */
  1091.         adding = true;
  1092.         if (!tool)
  1093.             extSelect = true;
  1094.         else
  1095.             DoTreeSelect(root, SELECTOFF);        /* Turn off all old selections. */
  1096.         click->offset.h = 0;
  1097.         click->offset.v = 0;
  1098.         click->grabber  = 0;
  1099.         click->oldFlip  = 0;
  1100.         click->newFlip  = 0;
  1101.  
  1102.         i = (extSelect) ? EXTSELECTOBJ : ((RECTOBJ - 1) + tool);
  1103.         hndl = NewChild(NO_EDIT, root, cnum = 0, i, 0);
  1104.             /* The child gets created selected, ready to go, except that it's rect is
  1105.             ** empty.  As the user drags the object out, this will change. */
  1106.  
  1107.         if (!keepTool) {
  1108.             tool    = 0;
  1109.             newTool = true;
  1110.         }            /* If the tool is a one-use tool, dispose it. */
  1111.  
  1112.         if (hndl) {        /* If we succeeded at creating something... */
  1113.             rct.top  = rct.bottom = click->localEvent.where.v;
  1114.             rct.left = rct.right  = click->localEvent.where.h;
  1115.             DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);
  1116.             DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1117.         }
  1118.     }
  1119.     else {
  1120.         NewDocumentUndo(frHndl);
  1121.         cnum = GetChildNum(hndl);
  1122.         if (ModifyChild(SIZE_EDIT, root, cnum, false)) return;
  1123.             /* Out of memory.  I would handle this case better if it weren't just sample code.
  1124.             ** Given that there are no complex objects, we actually can't run out of memory
  1125.             ** due to this operation.  However, there may someday be a pixmap type of object,
  1126.             ** whose memory hit could be substantial. */
  1127.     }
  1128.  
  1129.     drawInfo.root = root;        /* The tree we are operating with. */
  1130.     drawInfo.cnum = cnum;        /* The backmost object that is changing. */
  1131.  
  1132.     if (hndl) {
  1133.         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1134.         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1135.         if (!err)
  1136.             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1137.         if (err) DisposeThisAndBelowLayers(wklyr);
  1138.             /* The above code creates the necessary offscreen layers for the following editing.
  1139.             ** The window layer is bound to succeed at getting created, as it uses the window,
  1140.             ** instead of creating an offscreen GWorld.  The work layer and back layer creations
  1141.             ** may fail.  If they do, then we will only have a window layer.  The window layer
  1142.             ** is smart enough to notice that it is alone, and if it is, then it will do the
  1143.             ** drawing directly to the window.  Drawing directly to the window means that there
  1144.             ** will be a bunch of flicker, but what else is there to do when there is not enough
  1145.             ** ram?  Drawing nothing is even worse. */
  1146.  
  1147.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1148.             /* Bounding rect of the object getting created or resized. */
  1149.  
  1150.         if (click->grabber)
  1151.             InsetRect(&rct, -3, -3);
  1152.         InvalLayer(wlyr, rct, true);
  1153.             /* If getting resized, then the object has grabbers, and we have to make sure
  1154.             ** that the area getting redrawn is large enough for the grabbers. */
  1155.  
  1156.         origHndl = hndl;
  1157.         HandToHand((Handle *)&origHndl);
  1158.         while (StillDown()) {
  1159.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  1160.             oldHndl = hndl;
  1161.             HandToHand((Handle *)&oldHndl);
  1162.             if (DoTreeObjMethod(hndl, SIZEMESSAGE, (long)click)) {        /* If new size... */
  1163.                 DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&newRct);
  1164.                 if (!err) {        /* If we have all offscreen layers... */
  1165.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1166.                     if (click->grabber)
  1167.                         InsetRect(&rct, -3, -3);
  1168.                     InvalLayer(wlyr, rct, true);
  1169.                     UpdateLayer(wlyr);
  1170.                 }
  1171.                 else {            /* If we only have wlyr... */
  1172.                     SwapTreeObjData(hndl, oldHndl);
  1173.                     if (!EqualTreeObjData(hndl, origHndl))
  1174.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1175.                     SwapTreeObjData(hndl, oldHndl);
  1176.                     if (!EqualTreeObjData(hndl, origHndl))
  1177.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1178.                 }
  1179.             }
  1180.             DisposeHandle((Handle)oldHndl);
  1181.  
  1182.             GetMouse(&curMouse);
  1183.             GetContentRect(window, &rct);
  1184.             if (!PtInRect(curMouse, &rct)) {
  1185.  
  1186.                 GetContentOrigin(window, &org);
  1187.                 oldOrg = org;
  1188.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1189.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1190.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1191.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1192.  
  1193.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1194.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1195.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1196.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1197.  
  1198.                 rct = GetDataArea(root);
  1199.                 if (rct.right < curMouse.h)
  1200.                     rct.right = curMouse.h;
  1201.                 if (rct.bottom < curMouse.v)
  1202.                     rct.bottom = curMouse.v;
  1203.                 SetDataArea(root, rct.right, rct.bottom);
  1204.  
  1205.                 SetContentOrigin(window, org.h, org.v);
  1206.                 GetContentOrigin(window, &org);
  1207.  
  1208.                 if (!err) {
  1209.                     if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1210.                         DisposeThisAndBelowLayers(wlyr);
  1211.                         DoImageDocument(frHndl);
  1212.                         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1213.                         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1214.                         if (!err)
  1215.                             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1216.                         if (err) DisposeThisAndBelowLayers(wklyr);
  1217.                         GetContentRect(window, &rct);
  1218.                         InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1219.                     }
  1220.                 }
  1221.                 else
  1222.                     DoImageDocument(frHndl);
  1223.             }
  1224.             else {
  1225.                 rct = GetDataArea(root);
  1226.                 SetDataArea(root, rct.right, rct.bottom);
  1227.             }
  1228.         }
  1229.         if (origHndl)
  1230.             DisposeHandle((Handle)origHndl);
  1231.  
  1232.         rct = GetDataArea(root);
  1233.         SetDataArea(root, rct.right, rct.bottom);
  1234.  
  1235.         DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&rct);
  1236.         h = rct.bottom - rct.top;
  1237.         w = rct.right  - rct.left;
  1238.             /* This is how big the final product is.  It may now be unacceptably small.
  1239.             ** If we were adding a new object and it is too small, get rid of it entirely
  1240.             ** and use this to deselect the tool.  If we were resizing an existing object,
  1241.             ** then force the object to be a minimum size. */
  1242.  
  1243.         if (((!h) || (!w)) || ((h < 8) && (w < 8))) {    /* If smaller than minimum... */
  1244.             if (adding) {
  1245.                 DisposeChild(NO_EDIT, root, 0);        /* Don't create a new object that's too small. */
  1246.                 hndl = nil;                    /* We didn't change anything, so flag this situation.  */
  1247.                 SetPaletteTool(0);
  1248.                 newTool = false;
  1249.             }
  1250.             else {
  1251.                 rct.bottom = rct.top  + 8;                /* Don't let them shrink it too small. */
  1252.                 rct.right  = rct.left + 8;
  1253.                 DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);    /* Make it minimum. */
  1254.             }
  1255.             DoImageDocument(frHndl);
  1256.         }
  1257.  
  1258.         if (hndl) {        /* If we added or changed something... */
  1259.  
  1260.  
  1261.             if (adding) {        /* If we are adding, we haven't handled posting the addition
  1262.                                 ** for undo.  The reason for this is that the user may not
  1263.                                 ** actually add an object.  If they just click and release,
  1264.                                 ** there is no object added.  If we add the child with undo
  1265.                                 ** posting, and then remove it if the user doesn't grow it
  1266.                                 ** out, then we may lose some old undo info.  If the user
  1267.                                 ** doesn't grow out the object, the operation is a NOP, and
  1268.                                 ** so old undo information shouldn't be lost. */
  1269.                 if (extSelect) {                            /* We only created the object for extend-select purposes. */
  1270.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&extRct1);
  1271.                     for (i = (*root)->numChildren - 1; i; --i) {
  1272.                         cobj = GetChildHndl(root, i);
  1273.                         DoTreeObjMethod(cobj, GETBBOXMESSAGE, (long)&extRct2);
  1274.                         SectRect(&extRct1, &extRct2, &extRct3);
  1275.                         if (EqualRect(&extRct2, &extRct3))
  1276.                             DoTreeObjMethodClipped(cobj, SETSELECTMESSAGE, SELECTTOGGLE);
  1277.                     }
  1278.                     DisposeChild(NO_EDIT, root, 0);            /* We're done with it, so kill it. */
  1279.                 }
  1280.                 else {
  1281.                     NewDocumentUndo(frHndl);
  1282.                     hndl = CopyChild(NEW_EDIT, root, 0, root, 0, false);
  1283.                     DisposeChild(NO_EDIT, root, 1);
  1284.                 }    /* We just created a posted copy of the child, just as if we had called
  1285.                     ** NewChild.  We also disposed of the temporary unposted copy of the child.
  1286.                     ** The only problem is that objects entering the document get flagged as
  1287.                     ** selected.  This means that when we select the object, nothing happens
  1288.                     ** on the screen because the object thinks it is already selected. */
  1289.                 if (!extSelect)
  1290.                     if (hndl)
  1291.                         DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1292.                             /* Now the select grabbers can be drawn. */
  1293.             }
  1294.  
  1295.             rct = GetDataArea(root);
  1296.             SetDataArea(root, rct.right, rct.bottom);
  1297.  
  1298.             SetWindowDirty(window);
  1299.             if (err)
  1300.                 DoImageDocument(frHndl);
  1301.                     /* If we have only a wlyr, then we haven't really been generating a
  1302.                     ** complete image.  Redraw the document so that a full image is shown. */
  1303.  
  1304.             if (!extSelect)
  1305.                 DoTreeObjMethodClipped(hndl, SETSELECTMESSAGE, SELECTON);
  1306.                     /* Make sure the object is selected. */
  1307.         }
  1308.  
  1309.         DisposeThisAndBelowLayers(wlyr);        /* Clean up. */
  1310.     }
  1311.  
  1312.     if (extSelect)
  1313.         DoImageDocument(frHndl);
  1314.             /* Since we have killed the extend-select object, erase it from the screen. */
  1315.  
  1316.     if (newTool)
  1317.         SetPaletteTool(tool);
  1318. }
  1319.  
  1320.  
  1321.  
  1322. /*****************************************************************************/
  1323.  
  1324.  
  1325.  
  1326. #pragma segment DrawDoc
  1327. static OSErr    WindowLayerProc(LayerObj theLayer, short message)
  1328. {
  1329.     OSErr            err;
  1330.     WindowPtr        oldPort, window;
  1331.     Point            contOrg;
  1332.     Rect            contRct, thisUpdate;
  1333.     LayerDrawInfo    drawInfo;
  1334.     RgnHandle        oldClip, newClip;
  1335.  
  1336.     err = noErr;
  1337.  
  1338.     drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1339.  
  1340.     switch (message) {
  1341.         case kLayerInit:
  1342.             err = DefaultLayerProc(theLayer, message);
  1343.             GetPort(&oldPort);
  1344.             SetPort(window = (*theLayer)->layerPort);
  1345.             GetContentOrigin(window, &contOrg);
  1346.             SetOrigin(contOrg.h, contOrg.v);
  1347.             GetContentRect(window, &contRct);
  1348.             (*theLayer)->dstRect = contRct;
  1349.                 /* The above calculates the content rect (less tool palette and scrollbars).
  1350.                 ** By setting the dstRect to this smaller rect, work layer and back layer
  1351.                 ** will be created the size of dstRect, instead of the portRect of the
  1352.                 ** window.  This in turn will keep the GWLayers code from drawing over
  1353.                 ** the tool palette and scrollbars. */
  1354.             SetPort(oldPort);
  1355.             break;
  1356.         case kLayerUpdate:
  1357.             BeginContent((*theLayer)->layerPort);
  1358.             if ((*theLayer)->belowLayer)
  1359.                 DefaultLayerProc(theLayer, message);
  1360.             else {        /* If offscreen layers couldn't be made, then do it by hand. */
  1361.                 thisUpdate = UpdateUpdateRects(theLayer);
  1362.                 oldClip = NewRgn();
  1363.                 newClip = NewRgn();
  1364.                 RectRgn(newClip, &thisUpdate);
  1365.                 GetClip(oldClip);
  1366.                 SetClip(newClip);
  1367.                 EraseRect(&thisUpdate);
  1368.                 DoTreeDraw(drawInfo.root, DRAWOBJ);
  1369.                 DoTreeDraw(drawInfo.root, DRAWSELECT);
  1370.                 SetClip(oldClip);
  1371.                 DisposeRgn(oldClip);
  1372.                 DisposeRgn(newClip);
  1373.                 DrawPageGrid();
  1374.             }
  1375.             EndContent((*theLayer)->layerPort);
  1376.             break;
  1377.  
  1378.         default:
  1379.             err = DefaultLayerProc(theLayer, message);
  1380.                 /* Default behavior for everything else. */
  1381.             break;
  1382.     }
  1383.  
  1384.     return(err);
  1385. }
  1386.  
  1387.  
  1388.  
  1389. /*****************************************************************************/
  1390.  
  1391.  
  1392.  
  1393. #pragma segment DrawDoc
  1394. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1395. {
  1396.     OSErr            err;
  1397.     Rect            rct;
  1398.     LayerDrawInfo    drawInfo;
  1399.     short            cnum;
  1400.     TreeObjHndl        chndl;
  1401.  
  1402.     err = noErr;
  1403.  
  1404.     switch (message) {
  1405.         case kLayerInit:
  1406.             err = DefaultLayerProc(theLayer, message);
  1407.             break;
  1408.         case kLayerUpdate:
  1409.             SetLayerWorld(theLayer);
  1410.             drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1411.             if ((*theLayer)->belowLayer)
  1412.                 DefaultLayerProc(theLayer, message);
  1413.             else {
  1414.                 rct = GetEffectiveDstRect(theLayer);
  1415.                 EraseRect(&rct);
  1416.                 drawInfo.cnum = (*drawInfo.root)->numChildren - 1;
  1417.             }
  1418.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1419.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1420.                 DoTreeDraw(chndl, DRAWOBJ);
  1421.             }
  1422.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1423.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1424.                 DoTreeDraw(chndl, DRAWSELECT);
  1425.             }
  1426.             DrawPageGrid();
  1427.             ResetLayerWorld(theLayer);
  1428.             break;
  1429.         default:
  1430.             err = DefaultLayerProc(theLayer, message);
  1431.                 /* Default behavior for everything else. */
  1432.             break;
  1433.     }
  1434.  
  1435.     return(err);
  1436. }
  1437.  
  1438.  
  1439.  
  1440. /*****************************************************************************/
  1441.  
  1442.  
  1443.  
  1444. #pragma segment DrawDoc
  1445. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1446. {
  1447.     OSErr            err;
  1448.     Rect            rct;
  1449.     LayerDrawInfo    drawInfo;
  1450.     short            cnum;
  1451.     TreeObjHndl        chndl;
  1452.  
  1453.     err = noErr;
  1454.  
  1455.     switch (message) {
  1456.         case kLayerInit:
  1457.             err = DefaultLayerProc(theLayer, message);
  1458.             if (!err) {
  1459.                 SetLayerWorld(theLayer);
  1460.                 rct = GetEffectiveDstRect(theLayer);
  1461.                 EraseRect(&rct);
  1462.                 drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1463.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1464.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1465.                     DoTreeDraw(chndl, DRAWOBJ);
  1466.                 }
  1467.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1468.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1469.                     DoTreeDraw(chndl, DRAWSELECT);
  1470.                 }
  1471.                 ResetLayerWorld(theLayer);
  1472.             }
  1473.             break;
  1474.         default:
  1475.             err = DefaultLayerProc(theLayer, message);
  1476.                 /* Default behavior for everything else. */
  1477.             break;
  1478.     }
  1479.  
  1480.     return(err);
  1481. }
  1482.  
  1483.  
  1484.  
  1485. /*****************************************************************************/
  1486.  
  1487.  
  1488.  
  1489. #pragma segment DrawDoc
  1490. void    DrawPageGrid(void)
  1491. {
  1492.     WindowPtr    curPort;
  1493.     short        x1, x2, y1, y2, i;
  1494.  
  1495.     if (!gPrintPage) {
  1496.         GetPort(&curPort);
  1497.         PenPat((ConstPatternParam)&qd.gray);
  1498.         x1  = curPort->portRect.left   / (7 * 72);
  1499.         x1 *= (7 * 72);
  1500.         x2  = curPort->portRect.right  / (7 * 72);
  1501.         x2 *= (7 * 72);
  1502.         y1  = curPort->portRect.top    / (10 * 72);
  1503.         y1 *= (10 * 72);
  1504.         y2  = curPort->portRect.bottom / (10 * 72);
  1505.         y2 *= (10 * 72);
  1506.         for (i = x1; i <= x2; i += (7 * 72)) {
  1507.             MoveTo(i - 1, -1);
  1508.             Line  (0, 16383);
  1509.         }
  1510.         for (i = y1; i <= y2; i += (10 * 72)) {
  1511.             MoveTo(-1, i - 1);
  1512.             Line  (16383, 0);
  1513.         }
  1514.         PenNormal();
  1515.     }
  1516. }
  1517.  
  1518.  
  1519.  
  1520. /*****************************************************************************/
  1521.  
  1522.  
  1523.  
  1524. #pragma segment DrawDoc
  1525. void    SlideSelection(FileRecHndl frHndl, ClickInfo *click)
  1526. {
  1527.     LayerDrawInfo    drawInfo;
  1528.     WindowPtr        window;
  1529.     LayerObj        wlyr, wklyr, blyr;
  1530.     Point            lastMouse, curMouse;
  1531.     short            cnum, dh, dv;
  1532.     TreeObjHndl        root, chndl;
  1533.     Rect            newLoc, selectRct, rct;
  1534.     Point            oldOrg, org;
  1535.     Boolean            changed, autoScroll, dragDrop;
  1536.     OSErr            err;
  1537.     EventRecord        event;
  1538.     long            as, autoScrollTick;
  1539.  
  1540.     autoScrollTick = 0;
  1541.  
  1542.     changed = false;
  1543.     NewDocumentUndo(frHndl);
  1544.  
  1545.     root = (*frHndl)->d.doc.root;
  1546.     for (cnum = (*root)->numChildren;;) {
  1547.         if (DoTreeObjMethod(GetChildHndl(root, --cnum), GETSELECTMESSAGE, 0)) break;
  1548.         if (!cnum) return;
  1549.     }
  1550.  
  1551.     drawInfo.root = root;
  1552.     drawInfo.cnum = cnum;
  1553.     window        = (*frHndl)->fileState.window;
  1554.  
  1555.                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1556.               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1557.     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1558.  
  1559.     selectRct = GetSelectedArea(root);
  1560.     InsetRect(&selectRct, -3, -3);
  1561.     InvalLayer(wlyr, selectRct, false);
  1562.     lastMouse = click->localEvent.where;
  1563.  
  1564.     autoScroll = dragDrop = false;
  1565.     while (StillDown()) {
  1566.         GetMouse(&curMouse);
  1567.         GetContentRect(window, &rct);
  1568.         rct.top  += 16;
  1569.         rct.left += 16;
  1570.         as = autoScroll;
  1571.         autoScroll = !PtInRect(curMouse, &rct);
  1572.         if (autoScroll) {
  1573.             if (!autoScrollTick) autoScrollTick = TickCount();
  1574.             dh = lastMouse.h - curMouse.h;
  1575.             if (dh < 0) dh = -dh;
  1576.             dv = lastMouse.v - curMouse.v;
  1577.             if (dv < 0) dv = -dv;
  1578.             if ((dh + dv > 6) && (!as)) autoScrollTick = TickCount();
  1579.             if (autoScrollTick + 15 > TickCount()) autoScroll = false;
  1580.         }
  1581.         if (DragDropAvailable()) {
  1582.             InsetRect(&rct, -16, -16);
  1583.             dragDrop = !PtInRect(curMouse, &rct);
  1584.             InsetRect(&rct,  16,  16);
  1585.         }
  1586.  
  1587.         if ((dragDrop) || (click->localEvent.modifiers & optionKey)) {
  1588.             if (changed) RevertEdit(root, true);
  1589.             event = click->localEvent;
  1590.             LocalToGlobal(&event.where);
  1591.             if (DoDrag(frHndl, &event) != paramErr) break;
  1592.         }
  1593.  
  1594.         click->offset.h = (curMouse.h - lastMouse.h);
  1595.         click->offset.v = (curMouse.v - lastMouse.v);
  1596.         click->message  = CLICKDRAG;
  1597.  
  1598.         if ((autoScroll) || (click->offset.h) || (click->offset.v)) {
  1599.             changed = true;
  1600.             if ((click->offset.h) || (click->offset.v)) {
  1601.                 for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  1602.                     chndl = GetChildHndl(root, cnum);
  1603.                     if (mDerefCommon(chndl)->selected) {
  1604.                         ModifyChild(MOVE_EDIT, root, cnum, true);
  1605.                         DoFTreeMethod(chndl, CLICKMESSAGE, (long)click);
  1606.                         DoTreeObjMethod(chndl, GETBBOXMESSAGE, (long)&newLoc);
  1607.                         InsetRect(&newLoc, -3, -3);
  1608.                         InvalLayer(wlyr, newLoc, true);
  1609.                     }
  1610.                 }
  1611.                 UpdateLayer(wlyr);
  1612.             }
  1613.             lastMouse = curMouse;
  1614.  
  1615.             if (autoScroll) {
  1616.                 GetContentOrigin(window, &org);
  1617.                 oldOrg = org;
  1618.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1619.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1620.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1621.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1622.  
  1623.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1624.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1625.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1626.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1627.  
  1628.                 rct = GetDataArea(root);
  1629.                 if (rct.right < curMouse.h)
  1630.                     rct.right = curMouse.h;
  1631.                 if (rct.bottom < curMouse.v)
  1632.                     rct.bottom = curMouse.v;
  1633.                 SetDataArea(root, rct.right, rct.bottom);
  1634.  
  1635.                 SetContentOrigin(window, org.h, org.v);
  1636.                 GetContentOrigin(window, &org);
  1637.                 if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1638.                     DisposeThisAndBelowLayers(wlyr);
  1639.                     DoImageDocument(frHndl);
  1640.                                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1641.                               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1642.                     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1643.                     if (err) DisposeThisAndBelowLayers(wklyr);
  1644.                     GetContentRect(window, &rct);
  1645.                     InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1646.                 }
  1647.             }
  1648.             else {
  1649.                 rct = GetDataArea(root);
  1650.                 SetDataArea(root, rct.right, rct.bottom);
  1651.             }
  1652.         }
  1653.     }
  1654.  
  1655.     rct = GetDataArea(root);
  1656.     SetDataArea(root, rct.right, rct.bottom);
  1657.  
  1658.     if (changed)
  1659.         SetWindowDirty(window);
  1660.  
  1661.     DisposeThisAndBelowLayers(wlyr);
  1662. }
  1663.  
  1664.  
  1665.  
  1666. /*****************************************************************************/
  1667.  
  1668.  
  1669.  
  1670. #pragma segment DrawDoc
  1671. void    NewDocumentUndo(FileRecHndl frHndl)
  1672. {
  1673.     NewUndo((*frHndl)->d.doc.root);
  1674. }
  1675.  
  1676.  
  1677.  
  1678.